﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Media;
using System.Windows;
using System.Windows.Media.Effects;

namespace BinaryTimesCore
{
    public class TextElementAnimation
    {
        #region fields

        static int instanceCounter;

        Storyboard storyboard;
        Canvas canvasParent;

        Dictionary<TextElement, TextBlock> textElementTextBlocks;

        #endregion

        #region events

        public event EventHandler AnimationCompleted;

        protected virtual void OnAnimationCompleted(object sender, EventArgs e)
        {
            EventHandler temp = AnimationCompleted;
            if (temp != null)
            {
                temp(sender, e);
            }
        }

        #endregion


        public TextElementAnimation(Canvas canvasParent, params TextElement[] textElements)
        {
            instanceCounter++;
            this.canvasParent = canvasParent;

            textElementTextBlocks = new Dictionary<TextElement, TextBlock>();

            storyboard = new Storyboard();
            storyboard.Completed += storyboard_Completed;


            InitializeTextElements(textElements);
        }

        public void Start()
        {
            storyboard.Begin(canvasParent);
        }


        void InitializeTextElements(IEnumerable<TextElement> textElements)
        {
            int elementCounter = 0;

            foreach (var textElement in textElements)
            {
                TextBlock textBlock = new TextBlock
                {
                    Name = string.Format("textBlock_{0}_{1}", elementCounter++, instanceCounter),
                    Foreground = new SolidColorBrush(Color.FromArgb(0x80, 0xff, 0xff, 0xff)),
                    FontSize = 40,
                    FontWeight = FontWeights.Bold,
                    FontFamily = new FontFamily("Arial Black"),
                    TextAlignment = TextAlignment.Justify,
                    Visibility = Visibility.Hidden,
                    Effect = new BlurEffect { Radius = 3 }
                };

                canvasParent.RegisterName(textBlock.Name, textBlock);
                canvasParent.Children.Add(textBlock);

                textElementTextBlocks.Add(textElement, textBlock);

                DoubleAnimation doubleAnimationLocation = new DoubleAnimation();
                DoubleAnimation doubleAnimationOpacity = new DoubleAnimation { From = 1.0, To = 0.1, AccelerationRatio = 0.9 };


                storyboard.Children.Add(doubleAnimationLocation);
                storyboard.Children.Add(doubleAnimationOpacity);

                Storyboard.SetTargetName(doubleAnimationLocation, textBlock.Name);

                Storyboard.SetTargetName(doubleAnimationOpacity, textBlock.Name);
                Storyboard.SetTargetProperty(doubleAnimationOpacity, new PropertyPath(TextBlock.OpacityProperty));


                if (textElement.Mode == TextElementAnimationMode.BottomUp || textElement.Mode == TextElementAnimationMode.TopDown)
                    textElement.Text = Helper.ConvertTextToVertical(textElement.Text);
                else
                    textElement.Text = Helper.AddSpacesToText(textElement.Text, 1);

                textBlock.Text = textElement.Text;

                textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                textBlock.Arrange(new Rect(new Point(0, 0), textBlock.DesiredSize));

                double startValue = 0;
                double endValue = 0;

                switch (textElement.Mode)
                {
                    case TextElementAnimationMode.LeftToRight:
                        {
                            startValue = -textBlock.ActualWidth;
                            endValue = canvasParent.ActualWidth + 1;

                            Canvas.SetLeft(textBlock, startValue);
                            Canvas.SetTop(textBlock, textElement.FixedLocation.Y);
                            Storyboard.SetTargetProperty(doubleAnimationLocation, new PropertyPath(Canvas.LeftProperty));
                            break;
                        }
                    case TextElementAnimationMode.TopDown:
                        {
                            startValue = -textBlock.ActualHeight;
                            endValue = canvasParent.ActualHeight + 1;

                            Canvas.SetTop(textBlock, startValue);
                            Canvas.SetLeft(textBlock, textElement.FixedLocation.X);
                            Storyboard.SetTargetProperty(doubleAnimationLocation, new PropertyPath(Canvas.TopProperty));

                            break;
                        }
                    case TextElementAnimationMode.RightToLeft:
                        {
                            startValue = canvasParent.ActualWidth + 1;
                            endValue = -textBlock.ActualWidth;

                            Canvas.SetLeft(textBlock, startValue);
                            Canvas.SetTop(textBlock, textElement.FixedLocation.Y);
                            Storyboard.SetTargetProperty(doubleAnimationLocation, new PropertyPath(Canvas.LeftProperty));
                            break;
                        }
                    case TextElementAnimationMode.BottomUp:
                        {
                            startValue = canvasParent.ActualHeight + 1;
                            endValue = -textBlock.ActualHeight;

                            Canvas.SetTop(textBlock, startValue);
                            Canvas.SetLeft(textBlock, textElement.FixedLocation.X);
                            Storyboard.SetTargetProperty(doubleAnimationLocation, new PropertyPath(Canvas.TopProperty));

                            break;
                        }
                }

                textBlock.Visibility = Visibility.Visible;

                doubleAnimationLocation.From = startValue;
                doubleAnimationLocation.To = endValue;

                doubleAnimationLocation.Duration = textElement.Duration;
                doubleAnimationLocation.BeginTime = textElement.BeginTime;

                doubleAnimationOpacity.Duration = textElement.Duration;
                doubleAnimationOpacity.BeginTime = textElement.BeginTime;
            }
        }

        void storyboard_Completed(object sender, EventArgs e)
        {
            foreach (var textBlock in textElementTextBlocks.Values)
            {
                textBlock.Visibility = Visibility.Hidden;
                canvasParent.UnregisterName(textBlock.Name);
                canvasParent.Children.Remove(textBlock);
            }

            OnAnimationCompleted(this, EventArgs.Empty);
        }
    }

    public class TextElement
    {
        public TimeSpan? BeginTime { get; set; }
        public Duration Duration { get; set; }
        public Point FixedLocation { get; set; }
        public TextElementAnimationMode Mode { get; set; }
        public string Text { get; set; }
    }
}
